Setting up Ramaze on Lighttpd2 with FCGI and Runit

2011-10-10

A few days ago, when I was searching for a way to make a Ramaze application available via proxy forwarding while also letting lighttpd handle X-Sendfile, I was told that I'd have to use 1.5 or 2.0 to do that.

After a little digging it became clear that 1.5 would be a dead end, so i took the plunge and looked into the new and shiny version 2.

So let's have a look at each of the parts. First we start with lighttpd, and the new configuration. Please note that this is only for my own site, your mileage may vary.

I'm using Arch Linux for this, with runit as PID 1, as explained by bougyman.

The most important things are forwarding to fastcgi and setting the PATH_INFO header, as the default lighttpd one is not suitable for Ramaze.

setup {
  module_load ( "mod_balance", "mod_expire", "mod_fastcgi", "mod_vhost", "mod_lua", "mod_accesslog" );
  lua.plugin "core.lua";

  listen "0.0.0.0:80";
  listen "[::]:80";

  log ["debug" => "", "*" => "/var/log/lighttpd2/error.log"];
  accesslog "/var/log/lighttpd2/access.log";
  accesslog.format "%h %V %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"";

  static.exclude_extensions ( ".php", ".pl", ".fcgi", "~", ".inc" );
}

include "/etc/lighttpd2/mimetypes.conf";

vhost.map [
  "manveru.name" => {
    docroot "/home/manveru/github/manveru/manveru.name/public/";
    index ("index.html");

    # deliver static files directly, forward everything else to fcgi
    if physical.is_file {
      header.add ("X-cleanurl", "hit");
    } else {
      header.add ("X-cleanurl", "miss");
      env.set "PATH_INFO" => "%{enc:req.path}";

      balance.rr (
        {fastcgi "unix:/var/run/lighttpd/sockets/manveru.name/0.sock";},
        {fastcgi "unix:/var/run/lighttpd/sockets/manveru.name/1.sock";},
        {fastcgi "unix:/var/run/lighttpd/sockets/manveru.name/2.sock";}
      );
    }

    # if req.path =~ "\.(png|css|gif)$" { expire "access 1 week"; }
  }
];

static;

Next up is the Ramaze configuration, in this case we make one from scratch, just to make things a little bit more transparent and easier to control.

Eventually I want to make Ramaze easier to use with FCGI, but handling unix sockets for you is probably fraught with too many issues to provide a one-size-fits-all solution.

#!/usr/bin/env ruby

require 'ramaze'

## FCGI doesn't like you writing to stdout
Ramaze::Log.loggers = [Ramaze::Logger::Informer.new(__DIR__("../ramaze.fcgi.log"))]

require_relative '../app'

## Initialize Ramaze, but don't start any server just yet.
Ramaze.options.trap = :SIGTERM # will need that for runit later.
Ramaze.start(root: __DIR__('../'), started: true)

socket = File.join(*ENV.values_at('RAMAZE_SOCKET', 'RAMAZE_SOCKET_NUMBER'))
socket << '.sock'
puts "Connecting to #{socket}"

## make sure the socket is closed.
FileUtils.rm_f(socket)

Thread.new do
  begin
    sleep 0.5 until File.socket?(socket)
    File.chmod(0660, socket)
    File.chown(Etc.getpwnam(Etc.getlogin).uid, Etc.getgrnam('www-data').gid, socket)
  rescue Exception => ex
    Ramaze::Log.error(ex)
    raise(ex)
  end
end

Rack::Handler.get(:fastcgi).run(Ramaze, File: socket)